package com.backend.modules.task.service.impl;

import com.backend.common.enums.DeptType;
import com.backend.common.exception.BusinessException;
import com.backend.common.utils.SecurityUtils;
import com.backend.modules.system.entity.SysDepartment;
import com.backend.modules.system.entity.SysUser;
import com.backend.modules.system.repository.SysTeacherClassRepository;
import com.backend.modules.system.service.SysDepartmentService;
import com.backend.modules.system.service.SysUserService;
import com.backend.modules.task.dto.TaskDTO;
import com.backend.modules.task.dto.TaskQueryDTO;
import com.backend.modules.task.entity.Task;
import com.backend.modules.task.repository.TaskRepository;
import com.backend.modules.task.service.TaskService;
import com.backend.modules.task.vo.TaskStatisticsVO;
import com.backend.modules.task.convert.TaskSubmissionDetailConvert;
import com.backend.modules.task.entity.TaskSubmission;
import com.backend.modules.task.repository.TaskSubmissionRepository;
import com.backend.modules.task.vo.TaskSubmissionDetailVO;
import com.backend.modules.notification.enums.NotificationType;
import com.backend.modules.notification.service.NotificationService;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.ss.usermodel.Row;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Predicate;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.http.ResponseEntity;
import com.backend.common.utils.ExcelUtils;
import com.backend.modules.task.dto.SubmittedFileExcelDTO;
import com.backend.modules.task.dto.UnsubmittedStudentExcelDTO;
import com.backend.modules.task.entity.TaskSubmissionFile;
import java.io.OutputStream;
import java.net.URL;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Service
@Slf4j
@RequiredArgsConstructor
public class TaskServiceImpl implements TaskService {

    private static final Logger log = LoggerFactory.getLogger(TaskServiceImpl.class);

    private final TaskRepository taskRepository;
    private final SysDepartmentService departmentService;
    private final SysTeacherClassRepository teacherClassRepository;
    private final TaskSubmissionRepository submissionRepository;
    private final NotificationService notificationService;
    private final SysUserService sysUserService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void create(TaskDTO dto) {
        SysUser currentUser = sysUserService.getCurrentUser();
        // 校验班级 - 简化为单个班级
        SysDepartment classInfo = departmentService.getById(dto.getClassId());
        if (classInfo == null) {
            throw new BusinessException("班级不存在");
        }

        // 创建任务
        Task task = new Task();
        task.setName(dto.getName());
        task.setDescription(dto.getDescription());
        task.setDeadline(dto.getDeadline());
        task.setClassInfo(classInfo);
        task.setCreatorId(SecurityUtils.getCurrentUserId());
        task.setCreateTime(LocalDateTime.now());
        task.setFileRequirements(dto.getFileRequirements());
        task.setSubmitRequirements(dto.getSubmitRequirements());
        task.setTeacher(currentUser);
        
        Task savedTask = taskRepository.save(task);
        
        // 发送新任务通知给班级的学生
        String title = "新任务通知：" + task.getName();
        String content = String.format("您有一个新的任务需要完成，截止时间：%s", task.getDeadline());
        classInfo.getStudents().forEach(student -> 
            notificationService.send(
                student.getId(),
                title,
                content,
                NotificationType.NEW_TASK,
                savedTask.getId()
            )
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(Long id, TaskDTO dto) {
        Task task = getById(id);
        
        // 只能修改自己创建的任务
        if (!task.getCreatorId().equals(SecurityUtils.getCurrentUserId())) {
            throw new BusinessException("无权修改此任务");
        }
        
        // 更新任务信息
        task.setName(dto.getName());
        task.setDescription(dto.getDescription());
        task.setDeadline(dto.getDeadline());
        task.setFileRequirements(dto.getFileRequirements());
        task.setSubmitRequirements(dto.getSubmitRequirements());
        task.setUpdateTime(LocalDateTime.now());
        task.setUpdaterId(SecurityUtils.getCurrentUserId());
        
        // 更新班级 - 修改为单个班级
        SysDepartment classInfo = departmentService.getById(dto.getClassId());
        if (classInfo == null) {
            throw new BusinessException("班级不存在");
        }
        task.setClassInfo(classInfo);
        
        Task updatedTask = taskRepository.save(task);
        
        // 发送任务更新通知给班级的学生
        String title = "任务更新通知：" + task.getName();
        String content = "任务内容已更新，请查看最新要求。";
        
        classInfo.getStudents().forEach(student -> 
            notificationService.send(
                student.getId(),
                title,
                content,
                NotificationType.TASK_UPDATE,
                updatedTask.getId()
            )
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        Task task = getById(id);
        taskRepository.delete(task);
    }

    @Override
    public Task getById(Long id) {
        return taskRepository.findById(id)
                .orElseThrow(() -> new BusinessException("任务不存在"));
    }

    @Override
    public Page<Task> page(TaskQueryDTO query, Pageable pageable) {
        Specification<Task> spec = (root, criteriaQuery, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            // 筛选教师自己创建的任务
            predicates.add(cb.equal(root.get("teacherId"),
                SecurityUtils.getCurrentUserId()));
            // 名称模糊查询
            if (StringUtils.hasText(query.getName())) {
                predicates.add(cb.like(root.get("name"), 
                    "%" + query.getName() + "%"));
            }
            
            // 班级筛选
            // 班级筛选（增加空集合校验）
            Optional.ofNullable(query.getClassIds())
                .filter(ids -> !ids.isEmpty())
                .ifPresent(ids ->
                    predicates.add(root.get("classId").in(ids)));

            // 时间范围查询
            if (query.getStartTime() != null) {
                predicates.add(cb.greaterThanOrEqualTo(
                    root.get("deadline"), query.getStartTime()));
            }
            if (query.getEndTime() != null) {
                predicates.add(cb.lessThanOrEqualTo(
                    root.get("deadline"), query.getEndTime()));
            }

            // 任务状态查询
           if (query.getTaskStatus() != null){
               switch (query.getTaskStatus()) {
                   case "ENDED":
                       predicates.add(cb.lessThanOrEqualTo(
                           root.get("deadline"), LocalDateTime.now()));
                       break;
                   case "IN_PROGRESS":
                       predicates.add(cb.greaterThanOrEqualTo(
                           root.get("deadline"), LocalDateTime.now()));
                       break;
                   default:
                       break;
               }
           }
            
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        
        return taskRepository.findAll(spec, pageable);
    }

    @Override
    public TaskStatisticsVO getStatistics(Long taskId) {
        Task task = getById(taskId);
        log.info("获取任务统计信息，任务ID：{} task {}", taskId, task);
        // 获取班级总人数
        int totalCount = task.getClassInfo().getStudents().size();
        
        // 获取已提交人数
        int submittedCount = submissionRepository.countSubmittedStudentsByTaskId(taskId);
        // 计算未提交人数和提交率
        int unsubmittedCount = totalCount - submittedCount;
        String submissionRate = totalCount > 0 
                ? String.format("%.2f%%", (submittedCount * 100.0 / totalCount))
                : "0%";
        
        TaskStatisticsVO vo = new TaskStatisticsVO();
        vo.setTotalCount(totalCount);
        vo.setSubmittedCount(submittedCount);
        vo.setUnsubmittedCount(unsubmittedCount);
        vo.setSubmissionRate(submissionRate);
        
        return vo;
    }

    @Override
    public Page<TaskSubmissionDetailVO> getSubmissions(Long taskId, String studentName, 
            Boolean isLate, Pageable pageable) {
        Task task = getById(taskId);
        
        Specification<TaskSubmission> spec = (root, criteriaQuery, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            
            // 任务ID
            predicates.add(cb.equal(root.get("taskId"), taskId));
            
            // 学生姓名
            if (StringUtils.hasText(studentName)) {
                predicates.add(cb.like(
                    root.join("student").get("name"), 
                    "%" + studentName + "%"
                ));
            }
            
            // 是否逾期
            if (isLate != null) {
                if (isLate) {
                    predicates.add(cb.greaterThan(
                        root.get("submitTime"), 
                        task.getDeadline()
                    ));
                } else {
                    predicates.add(cb.lessThanOrEqualTo(
                        root.get("submitTime"), 
                        task.getDeadline()
                    ));
                }
            }
            
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        
        return submissionRepository.findAll(spec, pageable)
                .map(TaskSubmissionDetailConvert::convert);
    }

    @Override
    public void exportSubmissions(Long taskId, HttpServletResponse response) throws IOException {
        Task task = getById(taskId);
        
        // 设置响应头
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode(task.getName() + "-提交记录", "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        
        // 创建Excel工作簿
        try (XSSFWorkbook workbook = new XSSFWorkbook()) {
            XSSFSheet sheet = workbook.createSheet("提交记录");
            
            // 创建表头
            Row headerRow = sheet.createRow(0);
            headerRow.createCell(0).setCellValue("学号");
            headerRow.createCell(1).setCellValue("姓名");
            headerRow.createCell(2).setCellValue("提交时间");
            headerRow.createCell(3).setCellValue("是否逾期");
            headerRow.createCell(4).setCellValue("文件链接");
            headerRow.createCell(5).setCellValue("提交说明");
            
            // 查询所有提交记录（按提交时间倒序）
            List<TaskSubmission> submissions = submissionRepository.findByTaskIdOrderBySubmitTimeDesc(taskId);
            
            // 填充数据
            int rowNum = 1;
            for (TaskSubmission submission : submissions) {
                Row row = sheet.createRow(rowNum++);
                row.createCell(0).setCellValue(submission.getStudent().getUsername());
                row.createCell(1).setCellValue(submission.getStudent().getRealName());
                row.createCell(2).setCellValue(submission.getSubmitTime().toString());
                row.createCell(3).setCellValue(
                    submission.getSubmitTime().isAfter(task.getDeadline()) ? "是" : "否"
                );
                row.createCell(5).setCellValue(submission.getComment());
            }
            
            // 写入响应流
            workbook.write(response.getOutputStream());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void batchDelete(List<Long> ids) {
        // 验证任务是否都存在
        List<Task> tasks = taskRepository.findAllById(ids);
        if (tasks.size() != ids.size()) {
            throw new BusinessException("部分任务不存在");
        }
        
        // 删除任务
        taskRepository.deleteAllById(ids);
    }
    
    /**
     * 验证班级是否存在且教师是否有权限
     */
    private SysDepartment validateClass(Long classId) {
        // 获取班级信息
        SysDepartment classInfo = departmentService.getById(classId);
        if (classInfo == null) {
            throw new BusinessException("班级不存在");
        }
        
        // 验证是否是班级
        if (!classInfo.getType().equals(DeptType.CLASS)) {
            throw new BusinessException("只能选择班级");
        }
        
        // 验证教师是否有权限
        Long teacherId = SecurityUtils.getCurrentUserId();
        List<Long> teacherClassIds = teacherClassRepository.findClassIdsByTeacherId(teacherId);
        if (!teacherClassIds.contains(classId)) {
            throw new BusinessException("无权操作该班级");
        }
        
        return classInfo;
    }

    @Override
    public ResponseEntity<byte[]> exportSubmittedFiles(Long taskId) {
        Task task = getById(taskId);
        List<TaskSubmission> submissions = submissionRepository.findByTaskIdOrderBySubmitTimeDesc(taskId);
        
        List<SubmittedFileExcelDTO> excelData = new ArrayList<>();
        for (TaskSubmission submission : submissions) {
            for (TaskSubmissionFile file : submission.getFiles()) {
                SubmittedFileExcelDTO dto = new SubmittedFileExcelDTO();
                dto.setUserCode(submission.getStudent().getUserCode());
                dto.setRealName(submission.getStudent().getRealName());
                dto.setFileName(file.getFileName());
                dto.setSubmitTime(submission.getSubmitTime().toString());
                dto.setFileSize(String.format("%.2f", file.getFileSize() / 1024.0));
                excelData.add(dto);
            }
        }
        
        return ExcelUtils.export(excelData, 
                task.getName() + "-已提交文件列表", 
                "文件列表", 
                SubmittedFileExcelDTO.class);
    }

    @Override
    public ResponseEntity<byte[]> exportUnsubmittedStudents(Long taskId) {
        Task task = getById(taskId);
        
        // 获取已提交的学生ID列表
        List<Long> submittedStudentIds = submissionRepository.findByTaskId(taskId)
                .stream()
                .map(submission -> submission.getStudent().getId())
                .collect(Collectors.toList());
        
        // 获取班级所有学生
        List<SysUser> allStudents = task.getClassInfo().getStudents();
        
        // 过滤出未提交的学生
        List<UnsubmittedStudentExcelDTO> excelData = allStudents.stream()
                .filter(student -> !submittedStudentIds.contains(student.getId()))
                .map(student -> {
                    UnsubmittedStudentExcelDTO dto = new UnsubmittedStudentExcelDTO();
                    dto.setUserCode(student.getUserCode());
                    dto.setRealName(student.getRealName());
                    dto.setClassName(task.getClassInfo().getName());
                    return dto;
                })
                .collect(Collectors.toList());
        
        return ExcelUtils.export(excelData, 
                task.getName() + "-未提交学生名单", 
                "未提交学生", 
                UnsubmittedStudentExcelDTO.class);
    }

    @Override
    public void exportSubmittedFilesAsZip(Long taskId, OutputStream outputStream) throws IOException {
        Task task = getById(taskId);
        if (task == null) {
            throw new RuntimeException("任务不存在");
        }

        try (ZipOutputStream zipOut = new ZipOutputStream(outputStream)) {
            // 获取所有提交记录的文件
            List<TaskSubmissionFile> files = submissionRepository.findByTaskId(taskId)
                .stream()
                .flatMap(submission -> submission.getFiles().stream())
                .collect(Collectors.toList());

            // 遍历文件并添加到ZIP中
            for (TaskSubmissionFile file : files) {
                try {
                    // 创建ZIP条目，使用学生姓名和提交ID作为子文件夹
                    String studentName = file.getSubmission().getStudent().getRealName();
                    String entryName = String.format("%s_%d/%s", 
                        studentName,
                        file.getSubmissionId(), 
                        file.getFileName());
                    zipOut.putNextEntry(new ZipEntry(entryName));

                    // 从URL下载文件并写入ZIP
                    URL url = new URL(file.getFileUrl());
                    IOUtils.copy(url.openStream(), zipOut);
                    zipOut.closeEntry();
                } catch (Exception e) {
                    log.error("添加文件到ZIP失败: {}", file.getFileName(), e);
                    // 继续处理下一个文件
                }
            }
        }
    }
}